package han.chensing.latos.game.util;

import han.chensing.latos.V;

public class MathUtil {

	/**
	 * Limit value from min to max
	 * @param min	min value
	 * @param value	target value
	 * @param max	max value
	 * @return		Limited value
	 */
	public static float range(float min,float value,float max){
		return Math.max(min,Math.min(max,value));
	}

	/**
	 * Get a distance that point that vertically away from a line
	 * @param lineX		lineX
	 * @param lineY		lineY
	 * @param degree	line angle, DEG
	 * @param pointX	pointX
	 * @param pointY	pointY
	 * @return			Point distance to the line
	 */
	public static float getDistanceOfPointToLine(
			float lineX,float lineY,float degree,
			float pointX,float pointY){
		float[] pointVInLine = getPosPointVerticalInLine(lineX, lineY, degree, pointX, pointY);
		return distance(pointVInLine[0],pointVInLine[1],pointX,pointY)
				* getPointSideFix(lineX, lineY, degree, pointX, pointY);
	}

	/**
	 * Get a side fix
	 * @param lineX		lineX
	 * @param lineY		lineY
	 * @param degree	line angle, DEG
	 * @param pointX	pointX
	 * @param pointY	pointY
	 * @return			1 if positive side of the line; 0 if negative side of the line
	 */
	public static int getPointSideFix(
			float lineX,float lineY,float degree,
			float pointX,float pointY){
		int gx=getGx(degree);
		double rad=Math.toRadians(degree);
		double sin=Math.sin(rad);
		double cos=Math.cos(rad);
		double xyDistance=0;
		double ncTan=0;
		switch(gx){
			case 1:
			case 3:
				ncTan=sin/cos;
				xyDistance=pointX-lineX;
				break;
			case 2:
			case 4:
				ncTan=cos/sin;
				xyDistance=pointY-lineY;
				break;
		}
		double deltaXY = ncTan * xyDistance;
		double alignment;
		int pnFix=0;
		switch (gx){
			case 1:
				alignment=lineY+deltaXY;
				pnFix=pointY>alignment?1:-1;
				break;
			case 3:
				alignment=lineY+deltaXY;
				pnFix=pointY<alignment?1:-1;
				break;
			case 2:
				alignment=lineX+deltaXY;
				pnFix=pointX<alignment?1:-1;
				break;
			case 4:
				alignment=lineX+deltaXY;
				pnFix=pointX>alignment?1:-1;
				break;
		}
		return pnFix;
	}

	/**
	 * Get the cross point with screen of a line
	 * @param lineX		lineX
	 * @param lineY		lineY
	 * @param degree 	line angle, DEG
	 * @return			Cross point with screen
	 */
	public static float[] getCrossPointWithScreen(float lineX,float lineY,float degree){
		int gx=getGx(degree);
		double rad=Math.toRadians(degree);
		double sin=Math.sin(rad);
		double cos=Math.cos(rad);
		double tanOrCot=0;
		switch(gx){
			case 1:
			case 3:
				tanOrCot=sin/cos;
				break;
			case 2:
			case 4:
				tanOrCot=cos/sin;
				break;
		}
		float[] out=new float[2];
		switch(gx){
			case 1:{
				out[0]= V.WIDTH;
				out[1]= (float) (lineY+(V.WIDTH-lineX)*tanOrCot);
				break;
			}
			case 2:{
				out[1]= V.HEIGHT;
				out[0]= (float) (lineX+tanOrCot*(V.HEIGHT-lineY));
				break;
			}
			case 3:{
				out[0]=0;
				out[1]= (float) (lineY-lineX*tanOrCot);
				break;
			}
			case 4:{
				out[0]= (float) (lineX-lineY*tanOrCot);
				out[1]=0;
			}
		}
		return out;
	}

	/**
	 * Get a "GX" of a degree
	 * @param degree	angle, DEG
	 * @return			The "GX"
	 */
	public static int getGx(float degree){
		if((degree>=315&&degree<=360)||(degree>=0&&degree<45)){
			return 1;
		}else if(degree>=45&&degree<135){
			return 2;
		}else if(degree>=135&&degree<225){
			return 3;
		}else if(degree>=225&&degree<315){
			return 4;
		}else{
			return -1;
		}
	}

	/**
	 * Fix a degree from 0 to 360
	 * @param degree	angle, DEG
	 * @return			Fixed degree
	 */
	public static float fixDegree(float degree){
		if(degree<0){
			return (degree+360)<0?degree+360:fixDegree(degree+360);
		}else if(degree>360){
			return (degree-360)<360?degree-360:fixDegree(degree-360);
		}else{
			return degree;
		}
	}

	/**
	 * Get a point that vertically away from a line
	 * @param lineX		lineX
	 * @param lineY		lineY
	 * @param degree	angle, DEG
	 * @param distance	distance to line
	 * @return			The point
	 */
	public static float[] getPosOutOfLine(float lineX, float lineY, float degree, float distance){
		double rad=Math.toRadians(degree);
		double cos=Math.cos(rad);
		double sin=Math.sin(rad);
		double da=cos*distance;
		double db=sin*distance;
		return new float[]{lineX+(float)da,lineY+(float)db};
	}

	/**
	 * Get the vertical foot from a point to a line
	 * @param lineX		lineX
	 * @param lineY		lineY
	 * @param degree	angle, DEG
	 * @param pointX	pointX
	 * @param pointY	pointY
	 * @return			The vertical foot
	 */
	public static float[] getPosPointVerticalInLine(
		float lineX,float lineY,float degree,
		float pointX,float pointY){
		int gx=getGx(degree);
		float[] pos=new float[2];
		double rad=Math.toRadians(degree);
		double sin=Math.sin(rad);
		double cos=Math.cos(rad);
		switch(gx){
			case 1:
			case 3:
			{
				double tan=sin/cos;
				double tmp=pointY-lineY-(pointX-lineX)*tan;
				pos[0]=(float)(pointX+tmp*cos*sin);
				pos[1]=(float)(pointY-tmp*cos*cos);
				break;
			}
			case 2:
			case 4:
			{
				double cot=cos/sin;
				double tmp=pointX-lineX-(pointY-lineY)*cot;
				pos[0]=(float)(pointX-tmp*sin*sin);
				pos[1]=(float)(pointY+tmp*sin*cos);
				break;
			}
		}
		return pos;
	}

	/**
	 * Judge that is a point in vertical judgement range
	 * @param lineX			lineX
	 * @param lineY			lineY
	 * @param degree		angle, DEG
	 * @param pointX		pointX
	 * @param pointY		pointY
	 * @param judgeWidth	judge width
	 * @return			true if the point is in the judge range, vise versa
	 */
	public static boolean isPointInJudgeRange(
			float lineX, float lineY, float degree,
			float pointX, float pointY, float judgeWidth){
		int gx=getGx(degree);
		double rad=Math.toRadians(degree);
		double sin,cos,cotOrTan;
		sin=Math.sin(rad);
		cos=Math.cos(rad);
		double p1=0,p2=0,ld1,ld2,d;
		switch(gx){
			case 1:
			case 3:
			{
				cotOrTan=sin/cos;
				ld1= judgeWidth /cos;
				d= pointY - lineY;
				ld2=d*cotOrTan;
				p1=lineX-(ld2+ld1);
				p2=lineX-(ld2-ld1);
				break;
			}
			case 2:
			case 4:
			{
				cotOrTan=cos/sin;
				ld1= judgeWidth /sin;
				d= pointY - lineY;
				ld2=d*cotOrTan;
				p1= lineY +(ld2+ld1);
				p2= lineY +(ld2-ld1);
				break;
			}
		}
		switch(gx){
			default:
				return false;
			case 1:
				return (pointX >=p1&& pointX <=p2);
			case 3:
				return (pointX >=p2&& pointX <=p1);
			case 2:
				return (pointY >=p2&& pointY <=p1);
			case 4:
				return (pointY >=p1&& pointY <=p2);
		}
	}

	/**
	 * Get distance between two points
	 * @param x1	x1
	 * @param y1	y1
	 * @param x2	x2
	 * @param y2	y2
	 * @return		The distance
	 */
	public static float distance(float x1,float y1,float x2,float y2){
		return (float)(Math.sqrt(Math.pow(x1-x2,2)+Math.pow(y1-y2,2)));
	}

	/**
	 * Fix a value from 0 to 1
	 * @param data	the data
	 * @return		Fixed value
	 */
	public static float f1t0able(float data){
		return range(0, data, 1);
	}
}
